package product

import (
	"context"
	"fmt"
	"github.com/cloudwego/hertz/pkg/common/hlog"
	jsoniter "github.com/json-iterator/go"
	"gorm.io/gen"
	"gorm.io/gen/field"
	"mall_admin_api/biz/dal"
	"mall_admin_api/biz/dal/memory"
	"mall_admin_api/biz/handler/common"
	"mall_admin_api/biz/model"
	"mall_admin_api/biz/model/admin_product"
	"mall_admin_api/biz/pack"
	"mall_admin_api/biz/util"
	"strings"
)

type Product struct {
	commonInfo *common.Response
	dao        *dal.InitDAO
}

func NewProduct(response *common.Response) *Product {
	return &Product{
		commonInfo: response,
		dao:        dal.NewInitDAO(response.GetBizInfo().ID),
	}
}

func (c *Product) GetList(ctx context.Context, req *admin_product.GetProductListRequest, page, size int) ([]*admin_product.Product, int64, error) {
	var conditions []gen.Condition
	conditions = append(conditions, dal.Product.BizID.Eq(c.commonInfo.GetBizInfo().ID))
	if req.Status != nil {
		conditions = append(conditions, dal.Product.Status.Value(req.Status))
	}
	if req.CategoryID != nil {
		conditions = append(conditions, dal.Product.CategoryID.Value(req.GetCategoryID()))
	}
	list, total, err := c.dao.ProductDAO.GetListWithTotal(ctx, &dal.ProductListParams{
		Condition: conditions,
		Page:      page,
		Size:      size,
	})
	if err != nil {
		hlog.CtxErrorf(ctx, "GetProductList err: %v", err)
		return nil, 0, err
	}
	var categoryIDs []int64
	var productIDs []int64
	for _, p := range list {
		categoryIDs = append(categoryIDs, p.CategoryID)
		productIDs = append(productIDs, p.ID)
	}
	categoryIDs = util.Unique(categoryIDs)
	categoryMap, err := memory.MGetCategory(ctx, c.commonInfo.GetBizInfo().ID, categoryIDs...)
	if err != nil {
		hlog.CtxErrorf(ctx, "GetProductList err: %v", err)
		return nil, 0, err
	}
	productItemList, err := c.dao.ProductItemDAO.GetProductItemList(ctx, []gen.Condition{dal.ProductItem.ProductID.In(productIDs...)}, 0, 0)
	if err != nil {
		hlog.CtxErrorf(ctx, "GetProductItemList err: %v", err)
		return nil, 0, err
	}
	return pack.GetProductList(list, categoryMap, productItemList), total, nil
}

func (c *Product) Update(ctx context.Context, req *model.UpdateProductRequest) error {
	var fields []field.AssignExpr
	fields = append(fields, dal.Product.UpdatedBy.Value(c.commonInfo.GetAdministratorInfo().Nickname))
	if req.Name != nil {
		fields = append(fields, dal.Product.Name.Value(*req.Name))
	}
	if req.Sort != nil {
		fields = append(fields, dal.Product.Sort.Value(*req.Sort))
	}
	if req.CategoryID != nil {
		fields = append(fields, dal.Product.CategoryID.Value(*req.CategoryID))
	}

	if len(req.Attrs) > 0 {
		var attrs = make([]map[string]interface{}, 0)
		for _, attr := range req.Attrs {
			if pic, ok := attr[pack.PicColumn]; ok {
				if i := util.Image2Path(pic.(string)); i != "" {
					attr[pack.PicColumn] = i
				}
			}
			attrs = append(attrs, attr)
		}
		specifications, err := jsoniter.MarshalToString(&attrs)
		if err != nil {
			hlog.CtxErrorf(ctx, "Create jsoniter MarshalToString  err: %v", err)
			return err
		}
		fields = append(fields, dal.Product.Specifications.Value(specifications))
	}

	if len(req.AttrsItem) > 0 {
		specificationsItems, err := jsoniter.MarshalToString(&req.AttrsItem)
		if err != nil {
			hlog.CtxErrorf(ctx, "Create jsoniter MarshalToString  err: %v", err)
			return err
		}
		fields = append(fields, dal.Product.SpecificationsItem.Value(specificationsItems))
	}

	if req.Description != nil {
		fields = append(fields, dal.Product.Description.Value(*req.Description))
	}
	if req.Image != nil {
		fields = append(fields, dal.Product.Image.Value(util.Image2Path(*req.Image)))
	}
	if req.Keyword != nil {
		fields = append(fields, dal.Product.Keyword.Value(*req.Keyword))
	}
	if req.ShortInfo != nil {
		fields = append(fields, dal.Product.ShortInfo.Value(*req.ShortInfo))
	}

	if len(req.SliderImage) > 0 {
		sliderImageArr := util.ImageArray2Path(req.SliderImage)
		sliderImage, err := jsoniter.MarshalToString(sliderImageArr)
		if err != nil {
			hlog.CtxErrorf(ctx, "Create jsoniter MarshalToString  err: %v", err)
			return err
		}
		fields = append(fields, dal.Product.SliderImage.Value(sliderImage))
	}
	if req.Sort != nil {
		fields = append(fields, dal.Product.Sort.Value(*req.Sort))
	}
	if req.SpecType != nil {
		fields = append(fields, dal.Product.SpecType.Value(req.SpecType))
	}
	if req.Status != nil {
		fields = append(fields, dal.Product.Status.Value(req.Status))
	}
	if req.UnitName != nil {
		fields = append(fields, dal.Product.UnitName.Value(*req.UnitName))
	}

	if err := dal.Q.Transaction(func(tx *dal.Query) error {
		if err := c.dao.ProductDAO.UpdateProductWithTx(ctx, tx, []gen.Condition{dal.Product.ID.Eq(req.ID)}, fields); err != nil {
			hlog.CtxErrorf(ctx, "Update err: %v", err)
			return err
		}
		if err := c.dao.ProductSpecDAO.DeleteProductSpecWithTx(ctx, tx, []gen.Condition{
			dal.ProductSpec.ProductID.Eq(req.ID),
			dal.ProductSpec.BizID.Eq(c.commonInfo.GetBizInfo().ID),
		}); err != nil {
			hlog.CtxErrorf(ctx, "DeleteProductSpecWithTx err: %v", err)
			return err
		}
		if err := c.dao.ProductSpecValueDAO.DeleteProductSpecValueWithTx(ctx, tx, []gen.Condition{
			dal.ProductSpecValue.ProductID.Eq(req.ID),
			dal.ProductSpecValue.BizID.Eq(c.commonInfo.GetBizInfo().ID),
		}); err != nil {
			hlog.CtxErrorf(ctx, "DeleteProductSpecValueWithTx err: %v", err)
			return err
		}
		if err := c.dao.ProductItemDAO.DeleteProductItemWithTx(ctx, tx, []gen.Condition{
			dal.ProductItem.ProductID.Eq(req.ID),
			dal.ProductItem.BizID.Eq(c.commonInfo.GetBizInfo().ID),
		}); err != nil {
			hlog.CtxErrorf(ctx, "DeleteProductItemWithTx err: %v", err)
			return err
		}

		if err := c.createProductItemAndSpecAndSPecValueWithTx(ctx, tx, req.ID, req.AttrsItem, req.Attrs); err != nil {
			hlog.CtxErrorf(ctx, "createProductItemAndSpecAndSPecValueWithTx err: %v", err)
			return err
		}
		return nil
	}); err != nil {
		hlog.CtxErrorf(ctx, "Transaction err: %v", err)
		return err
	}

	return nil
}

func (c *Product) Info(ctx context.Context, req *admin_product.GetProductInfoRequest) (*model.ProductInfo, error) {
	info, err := c.dao.ProductDAO.GetProductInfoByID(ctx, req.GetID())
	if err != nil {
		hlog.CtxErrorf(ctx, "Info err: %v", err)
		return nil, err
	}
	productSpecList, err := c.dao.ProductSpecDAO.GetProductSpecList(ctx, []gen.Condition{dal.ProductSpec.ProductID.Eq(req.GetID())}, 0, 0)
	if err != nil {
		hlog.CtxErrorf(ctx, "GetProductSpecList err: %v", err)
		return nil, err
	}
	productSpecItemList, err := c.dao.ProductSpecValueDAO.GetProductSpecValueList(ctx, []gen.Condition{dal.ProductSpecValue.ProductID.Eq(req.GetID())}, 0, 0)
	if err != nil {
		hlog.CtxErrorf(ctx, "GetProductSpecValueList err: %v", err)
		return nil, err
	}
	//
	//productItemList, err := c.productItemDAO.GetProductItemList(ctx, []gen.Condition{dal.ProductItem.ProductID.Eq(req.ID)}, 0, 0)
	//if err != nil {
	//	hlog.CtxErrorf(ctx, "GetProductItemList err: %v", err)
	//	return nil, err
	//}

	return pack.ProductInfo(info, productSpecList, productSpecItemList), nil
}

func (c *Product) Delete(ctx context.Context, req *admin_product.DeleteProductRequest) error {
	if err := c.dao.ProductDAO.DeleteProduct(ctx, []gen.Condition{dal.Product.ID.In(req.GetIDs()...)}); err != nil {
		hlog.CtxErrorf(ctx, "Delete err: %v", err)
		return err
	}
	return nil
}

func (c *Product) Create(ctx context.Context, req *model.ProductInfo) error {
	specificationsItems, err := jsoniter.MarshalToString(&req.AttrsItem)
	if err != nil {
		hlog.CtxErrorf(ctx, "Create jsoniter MarshalToString  err: %v", err)
		return err
	}

	var attrs = make([]map[string]interface{}, 0)
	for _, attr := range req.Attrs {
		if pic, ok := attr[pack.PicColumn]; ok {
			if i := util.Image2Path(pic.(string)); i != "" {
				attr[pack.PicColumn] = i
			}
		}
		attrs = append(attrs, attr)
	}
	specifications, err := jsoniter.MarshalToString(&attrs)
	if err != nil {
		hlog.CtxErrorf(ctx, "Create jsoniter MarshalToString  err: %v", err)
		return err
	}
	sliderImageArr := util.ImageArray2Path(req.SliderImage)
	sliderImage, err := jsoniter.MarshalToString(sliderImageArr)
	if err != nil {
		hlog.CtxErrorf(ctx, "Create jsoniter MarshalToString  err: %v", err)
		return err
	}
	var data = &model.Product{
		Name:               req.Name,
		BizID:              c.commonInfo.GetBizInfo().ID,
		Sort:               req.Sort,
		CreatedBy:          c.commonInfo.GetAdministratorInfo().Nickname,
		Image:              util.Image2Path(req.Image),
		SpecificationsItem: specificationsItems,
		Specifications:     specifications,
		CategoryID:         req.CategoryID,
		UnitName:           req.UnitName,
		Description:        req.Description,
		Status:             req.Status,
		Keyword:            req.Keyword,
		SpecType:           req.SpecType,
		ShortInfo:          req.ShortInfo,
		SliderImage:        sliderImage,
	}

	if err = dal.Q.Transaction(func(tx *dal.Query) error {
		var productInfo *model.Product
		productInfo, err = c.dao.ProductDAO.CreateProductWithTx(ctx, tx, data)
		if err != nil {
			hlog.CtxErrorf(ctx, "Create err: %v", err)
			return err
		}

		if err = c.createProductItemAndSpecAndSPecValueWithTx(ctx, tx, productInfo.ID, req.AttrsItem, req.Attrs); err != nil {
			hlog.CtxErrorf(ctx, "createProductItemAndSpecAndSPecValueWithTx err: %v", err)
			return err
		}

		return nil
	}); err != nil {
		hlog.CtxErrorf(ctx, "Transaction err: %v", err)
		return err
	}

	return nil
}

func (c *Product) createProductItemAndSpecAndSPecValueWithTx(ctx context.Context, tx *dal.Query, productID int64, attrItems []model.AttrItem, attrs []map[string]interface{}) error {
	var err error
	var allProductSpecValues []*model.ProductSpecValue
	for _, item := range attrItems {
		productSpec := &model.ProductSpec{
			Name:      item.Value,
			BizID:     c.commonInfo.GetBizInfo().ID,
			ProductID: productID,
		}
		// 规格名称
		productSpec, err = c.dao.ProductSpecDAO.CreateProductSpecWithTx(ctx, tx, productSpec)
		if err != nil {
			hlog.CtxErrorf(ctx, "CreateProductSpecWithTx err: %v", err)
			return err
		}
		var productSpecValues []*model.ProductSpecValue
		for _, specValue := range item.Detail {
			productSpecValues = append(productSpecValues, &model.ProductSpecValue{
				Name:          specValue,
				BizID:         c.commonInfo.GetBizInfo().ID,
				ProductID:     productID,
				ProductSpecID: productSpec.ID,
			})
		}
		if len(productSpecValues) > 0 {
			productSpecValues, err = c.dao.ProductSpecValueDAO.BatchCreateProductSpecValueWithTx(ctx, tx, productSpecValues)
			if err != nil {
				hlog.CtxErrorf(ctx, "BatchCreateWithTxProductSpecValueWithTx err: %v", err)
				return err
			}
			allProductSpecValues = append(allProductSpecValues, productSpecValues...)
		}
	}
	// 创建商品明细
	var productSpecValueMap = make(map[string]*model.ProductSpecValue)
	for _, value := range allProductSpecValues {
		productSpecValueMap[value.Name] = value
	}
	var productItemList []*model.ProductItem
	var attrKeys []string
	for i := 0; i < len(attrItems); i++ {
		attrKeys = append(attrKeys, fmt.Sprintf("value%d", i+1))
	}
	for _, attr := range attrs {
		var sku string
		var skuArray []string
		for _, key := range attrKeys {
			productSpecValue, ok := productSpecValueMap[attr[key].(string)]
			if ok {
				sku = fmt.Sprintf("%s%d%d", sku, productSpecValue.ProductSpecID, productSpecValue.ID)
				skuArray = append(skuArray, fmt.Sprintf("s-%d", productSpecValue.ProductSpecID), fmt.Sprintf("v-%d", productSpecValue.ID))
			}
		}
		if sku == "" {
			sku = fmt.Sprintf("%d:single", productID)
		}
		productItem := model.ProductItem{
			ProductID:   productID,
			BizID:       c.commonInfo.GetBizInfo().ID,
			SkuID:       sku,
			SkuValue:    strings.Join(skuArray, ":"),
			StoreNums:   int64(attr[pack.StockColumn].(float64)),
			MarketPrice: attr[pack.MarketPriceColumn].(float64),
			SellPrice:   attr[pack.PriceColumn].(float64),
			CostPrice:   attr[pack.CostPriceColumn].(float64),
			Weight:      attr[pack.WeightColumn].(float64),
		}
		if pic, ok := attr[pack.PicColumn]; ok {
			if i := util.Image2Path(pic.(string)); i != "" {
				productItem.ThumbImage = i
			}
		}
		productItemList = append(productItemList, &productItem)
	}
	if len(productItemList) > 0 {
		productItemList, err = c.dao.ProductItemDAO.BatchCreateProductItemWithTx(ctx, tx, productItemList)
		if err != nil {
			hlog.CtxErrorf(ctx, "BatchCreateProductItemWithTx err: %v", err)
			return err
		}
	}
	return nil
}
